﻿using HalconDotNet;
using MachineVision.Core.Extensions;
using MachineVision.Core.TemplateMatch.ShapeModel;
using Prism.Mvvm;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media.Media3D;

namespace MachineVision.Core.TemplateMatch.NccModel
{
    /// <summary>
    /// 相关性匹配服务
    /// </summary>
    public class NccModelService : BindableBase, ITemplateMatchService
    {
        public NccModelService()
        {
            Info = new MethodInfo()
            {
                Name = "find_ncc_model",
                Description = "Find the best matches of an NCC model in an image.",
                Parameters = new List<MathodParameter>
                {
                    new MathodParameter(){ Name = "Image", Description = "Input image in which the model should be found." },
                    new MathodParameter(){ Name = "ModelID", Description = "Handle of the model." },
                    new MathodParameter(){ Name = "AngleStart", Description = "Smallest rotation of the model." },
                    new MathodParameter(){ Name = "AngleExtent", Description = "Extent of the rotation angles." },
                    new MathodParameter(){ Name = "MinScore", Description = "Minimum score of the instances of the model to be found." },
                    new MathodParameter(){ Name = "NumMatches", Description = "Number of instances of the model to be found (or 0 for all matches)." },
                    new MathodParameter(){ Name = "MaxOverlap", Description = "Maximum overlap of the instances of the model to be found." },
                    new MathodParameter(){ Name = "SubPixel", Description = "Subpixel accuracy if not equal to 'none'." },
                    new MathodParameter(){ Name = "NumLevels", Description = "Number of pyramid levels used in the matching (and lowest pyramid level to use if |NumLevels| = 2)." },
                    new MathodParameter(){ Name = "Row", Description = "Row coordinate of the found instances of the model." },
                    new MathodParameter(){ Name = "Column", Description = "Column coordinate of the found instances of the model." },
                    new MathodParameter(){ Name = "Angle", Description = "Rotation angle of the found instances of the model." },
                    new MathodParameter(){ Name = "Score", Description = "Score of the found instances of the model." },
                },
                Predecessors = new List<String>
                {
                    "create_ncc_model",
                    "read_ncc_model",
                    "write_ncc_model",
                }
            };

            //初始化模版参数与运行参数的默认值
            TemplateParamter = new NccModelInputParameter();
            RunParamter = new NccModelRunParameter();
            RunParamter.ApplyDefaultParameter();
            TemplateParamter.ApplyDefaultParameter();

            this.Setting = new MatchResultSetting();
            this.Roi = new RoiParameter();
        }

        private HTuple modelID;
        HTuple hv_Row = new HTuple(), hv_Column = new HTuple();
        HTuple hv_Angle = new HTuple(), hv_Score = new HTuple();
        public MatchResultSetting Setting {  get; set; }
        public MethodInfo Info { get; set; }
        public RoiParameter Roi { get; set; }
        private NccModelInputParameter templateParamter;
        private NccModelRunParameter runParamter;

        /// <summary>
        /// 相关性匹配-模版参数
        /// </summary>
        public NccModelInputParameter TemplateParamter
        {
            get { return templateParamter; }
            set { templateParamter = value; RaisePropertyChanged(); }
        }

        /// <summary>
        /// 相关性匹配-运行参数
        /// </summary>
        public NccModelRunParameter RunParamter
        {
            get { return runParamter; }
            set { runParamter = value; RaisePropertyChanged(); }
        }

        /// <summary>
        /// 生成模版
        /// </summary>
        /// <param name="hObject"></param>
        /// <returns></returns>
        public async Task CreateTemplate(HObject image, HObject hObject)
        {
            await Task.Run(() =>
            {
                /*
                 * NCC匹配注意，创建模版时，需要将图像的格式转换为灰度图像
                 */
                var template = image.ReducDomain(hObject).CropDomain();

                HOperatorSet.CreateNccModel(template.Rgb1ToGray(),
                    TemplateParamter.NumLevels,
                    TemplateParamter.AngleStart,
                    TemplateParamter.AngleExtent,
                    TemplateParamter.AngleStep,
                    TemplateParamter.Metric, out modelID);
            });
        }

        /// <summary>
        /// 执行
        /// </summary>
        /// <param name="image"></param>
        public MatchResult Run(HObject image)
        {
            //用于存储模板匹配的结果。
            MatchResult matchResult = new MatchResult();
            matchResult.Results = new ObservableCollection<TemplateMatchResult>();

            if (image == null)
            {
                matchResult.Message = "输入图像无效";
                return matchResult;
            }

            if (modelID == null)
            {
                matchResult.Message = "输入模版无效";
                return matchResult;
            }

            var timeSpan = SetTimeHelper.SetTimer(() =>
            {
                //调用 ReducDomain 扩展方法来减少图像处理域。Roi 是一个包含感兴趣区域坐标的参数结构体。
                var imageReduced = image.ReducDomain(Roi);

                //执行形状模型匹配，将匹配结果存储在 hv_Row, hv_Column, hv_Angle, hv_Score 变量中。

                /*
                 * NCC匹配注意，创建结果时，需要将图像的格式转换为灰度图像
                 */
                HOperatorSet.FindNccModel(
                    imageReduced.Rgb1ToGray(),
                    modelID,
                    RunParamter.AngleStart,
                    RunParamter.AngleExtent,
                    RunParamter.MinScore,
                    RunParamter.NumMatches,
                    RunParamter.MaxOverlap,
                    RunParamter.SubPixel,
                    RunParamter.NumLevels,
                    out hv_Row, out hv_Column, out hv_Angle, out hv_Score);
            });

            //for循环把结果赋值给matchResult
            for (int i = 0; i < hv_Score.Length; i++)
            {
                matchResult.Results.Add(new TemplateMatchResult()
                {
                    Index = i + 1,
                    Row = hv_Row.DArr[i],
                    Column = hv_Column.DArr[i],
                    Angle = hv_Angle.DArr[i],
                    Score = hv_Score.DArr[i],
                    Contours = GetNccModelContours(modelID, hv_Row.DArr[i], hv_Column.DArr[i], hv_Angle.DArr[i], 0)
                });
            }

            matchResult.TimeSpane = timeSpan;
            matchResult.Setting = Setting;
            matchResult.Message = $"{DateTime.Now}: 匹配耗时: {timeSpan} ms，匹配个数: {matchResult.Results.Count}";

            if (matchResult.Results.Count > 0)
            {
                matchResult.IsSuccess = true;
            }

            //返回结果
            return matchResult;
        }

        /// <summary>
        /// 获取相关型匹配的结果轮廓
        /// </summary>
        /// <param name="hv_ModelID"></param>
        /// <param name="hv_Row"></param>
        /// <param name="hv_Column"></param>
        /// <param name="hv_Angle"></param>
        /// <param name="hv_Model"></param>
        /// <returns></returns>
        public HObject GetNccModelContours(HTuple hv_ModelID, HTuple hv_Row, HTuple hv_Column, HTuple hv_Angle, HTuple hv_Model)
        {

            HObject ho_ModelRegion = null, ho_ModelContours = null;
            HObject ho_ContoursAffinTrans = null, ho_Cross = null;


            HTuple hv_NumMatches = new HTuple(), hv_Index = new HTuple();
            HTuple hv_Match = new HTuple(), hv_HomMat2DIdentity = new HTuple();
            HTuple hv_HomMat2DRotate = new HTuple(), hv_HomMat2DTranslate = new HTuple();
            HTuple hv_RowTrans = new HTuple(), hv_ColTrans = new HTuple();
            HTuple hv_Model_COPY_INP_TMP = new HTuple(hv_Model);

            HOperatorSet.GenEmptyObj(out ho_ModelRegion);
            HOperatorSet.GenEmptyObj(out ho_ModelContours);
            HOperatorSet.GenEmptyObj(out ho_ContoursAffinTrans);
            HOperatorSet.GenEmptyObj(out ho_Cross);
            hv_NumMatches.Dispose();
            using (HDevDisposeHelper dh = new HDevDisposeHelper())
            {
                hv_NumMatches = new HTuple(hv_Row.TupleLength());
            }
            if ((int)(new HTuple(hv_NumMatches.TupleGreater(0))) != 0)
            {
                if ((int)(new HTuple((new HTuple(hv_Model_COPY_INP_TMP.TupleLength())).TupleEqual(0))) != 0)
                {
                    hv_Model_COPY_INP_TMP.Dispose();
                    HOperatorSet.TupleGenConst(hv_NumMatches, 0, out hv_Model_COPY_INP_TMP);
                }
                else if ((int)(new HTuple((new HTuple(hv_Model_COPY_INP_TMP.TupleLength())).TupleEqual(1))) != 0)
                {
                    {
                        HTuple ExpTmpOutVar_0;
                        HOperatorSet.TupleGenConst(hv_NumMatches, hv_Model_COPY_INP_TMP, out ExpTmpOutVar_0);
                        hv_Model_COPY_INP_TMP.Dispose();
                        hv_Model_COPY_INP_TMP = ExpTmpOutVar_0;
                    }
                }
                for (hv_Index = 0; (int)hv_Index <= (int)((new HTuple(hv_ModelID.TupleLength())) - 1); hv_Index = (int)hv_Index + 1)
                {
                    using (HDevDisposeHelper dh = new HDevDisposeHelper())
                    {
                        ho_ModelRegion.Dispose();
                        HOperatorSet.GetNccModelRegion(out ho_ModelRegion, hv_ModelID.TupleSelect(hv_Index));
                    }
                    ho_ModelContours.Dispose();
                    HOperatorSet.GenContourRegionXld(ho_ModelRegion, out ho_ModelContours, "border_holes");
                    HTuple end_val13 = hv_NumMatches - 1;
                    HTuple step_val13 = 1;
                    for (hv_Match = 0; hv_Match.Continue(end_val13, step_val13); hv_Match = hv_Match.TupleAdd(step_val13))
                    {
                        if ((int)(new HTuple(hv_Index.TupleEqual(hv_Model_COPY_INP_TMP.TupleSelect(hv_Match)))) != 0)
                        {
                            hv_HomMat2DIdentity.Dispose();
                            HOperatorSet.HomMat2dIdentity(out hv_HomMat2DIdentity);
                            using (HDevDisposeHelper dh = new HDevDisposeHelper())
                            {
                                hv_HomMat2DRotate.Dispose();
                                HOperatorSet.HomMat2dRotate(hv_HomMat2DIdentity, hv_Angle.TupleSelect(
                                    hv_Match), 0, 0, out hv_HomMat2DRotate);
                            }
                            using (HDevDisposeHelper dh = new HDevDisposeHelper())
                            {
                                hv_HomMat2DTranslate.Dispose();
                                HOperatorSet.HomMat2dTranslate(hv_HomMat2DRotate, hv_Row.TupleSelect(hv_Match), hv_Column.TupleSelect(hv_Match), out hv_HomMat2DTranslate);
                            }
                            ho_ContoursAffinTrans.Dispose();
                            HOperatorSet.AffineTransContourXld(ho_ModelContours, out ho_ContoursAffinTrans, hv_HomMat2DTranslate);
                        }
                    }
                }
            }
            ho_ModelRegion.Dispose();
            ho_ModelContours.Dispose();

            hv_Model_COPY_INP_TMP.Dispose();
            hv_NumMatches.Dispose();
            hv_Index.Dispose();
            hv_Match.Dispose();
            hv_HomMat2DIdentity.Dispose();
            hv_HomMat2DRotate.Dispose();
            hv_HomMat2DTranslate.Dispose();
            hv_RowTrans.Dispose();
            hv_ColTrans.Dispose();

            return ho_ContoursAffinTrans;
        }
    }
}
